﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Data;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Qetza.TeamFoundation.WorkItemQuery.Admin;
using Microsoft.TeamFoundation.Server;
using System.Diagnostics;


namespace WITD
{

    /// <summary>
    /// Specifies values that define the outcome of an CopyQuery call.
    /// </summary>
    public enum ImportResult
    {
        /// <summary>
        /// 
        /// </summary>
        Success = 1,                    //copy was successful
        /// <summary>
        /// 
        /// </summary>
        Skipped = 2,                    //copy was skipped
        /// <summary>
        /// 
        /// </summary>
        Exception = 3,                  //exception occurred 
        /// <summary>
        /// 
        /// </summary>
        MissingAreasOrIterations = 4,   //target Project has missing classification Nodes that are present in source Project and referenced in the query that failed
        /// <summary>
        /// 
        /// </summary>
        InvalidOnDup = 5                //invalid ondup action was specified and a duplicate query was encountered
    }

    /// <summary>
    /// 
    /// </summary>
    public enum GlobalListOptions
    {
        /// <summary>
        /// Makes the target global lists the same as the source.
        /// </summary>
        OverwriteAll = 0,
        /// <summary>
        /// Adds missing lists and list items to the existing target global lists.
        /// </summary>
        AddMissing = 1,
        /// <summary>
        /// Adds the global lists during export and then import.
        /// </summary>
        AddToWITDOnly = 2
    }

    /// <summary>
    /// 
    /// </summary>
    public enum QueryOptions
    {
        /// <summary>
        /// Copies all queries, overwriting queries with the same name.
        /// </summary>
        Overwrite = 0,
        /// <summary>
        /// Copies all queries except those with the same name.
        /// </summary>
        Skip = 1,
        /// <summary>
        /// Copies all queries, but renames the newly imported queries when it already exists in the target project.
        /// </summary>
        RenameNew = 2,
        /// <summary>
        /// Copies all queries, but renames the existing queries with the same name.
        /// </summary>
        RenameOld = 3
    }

    /// <summary>
    /// 
    /// </summary>
    public enum IterationOptions
    {
        /// <summary>
        /// Copies and overwrites all nodes. target = source.
        /// </summary>
        OverwriteAll = 0,
        /// <summary>
        /// Only adds nodes that are missing, keeping all existing nodes.
        /// </summary>
        AddMissing = 1
    }

    /// <summary>
    /// 
    /// </summary>
    public enum WITDOptions
    {
        /// <summary>
        /// Overwrites WITDs with the same name.
        /// </summary>
        Overwrite = 0,
        /// <summary>
        /// Skips WITD copy on duplicates.
        /// </summary>
        Skip = 1,
        /// <summary>
        /// Renames the new WITD before importing it.
        /// </summary>
        RenameNew = 2,
        /// <summary>
        /// Renames the existing WITD before importing the new one.
        /// </summary>
        RenameOld = 3
    }

    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        const Int32 MinToolTipLength = 50;

        const String MissingClassificationNodeErrorCode = "TF51011";
        const String SelectAllProjectsCaption = "Select All Projects as Targets";
        const String SelectNoProjectsCaption = "Deselect All Projects as Targets";
        const String SelectNoWITDsCaption = "Deselect All WITDs";
        const String SelectAllCaption = "Select All WITDs";
        const bool SameCollections = false;
        const bool DifferentCollections = true;
        const String LogName = "WIT_Import_";
        const String LogEntry = "{0} : {1} WITD [ {4} ] from source project [ {2} ] to target project [ {3} ]";
        const String InitLogEntry = "{0} : {1}";
        const String WITLogEntry = "{0} : {1} WITD [ {4} ] from source project [ {2} ] to target project [ {3} ]";
        const String RenameWITLogEntry = "{0} : {1} WITD [ {4} ] to [ {6} ]";
        const String DestroyWITLogEntry = "{0} : {1} WITD [ {4} ] from project [ {3} ] in collection [ {6} ]";
        const String ValidationLogEntry = "{0} : {1} WITD [ {4} ] from source project [ {2} ] to target project [ {3} ]";
        const String IterationExportLogEntry = "{0} : {1} from source project [ {2} ]";
        const String IterationImportLogEntry = "{0} : {1} to target project [ {3} ]";
        const String QueryImportLogEntry = "{0} : {1} from source project [ {2} ] to target project [ {3} ]";
        const String QueryDeleteLogEntry = "{0} : {1} from target project [ {3} ]";
        const String CategoryDeleteLogEntry = "{0} : {1} from target project [ {3} ]";
        const String CategoryImportLogEntry = "{0} : {1} from source project [ {2} ] into target project [ {3} ]";
        const String UpdateLinkTypesLogEntry = "{0} : {1} from source collection [ {5} ] into target collection [ {6} ]";
        const String UpdateGlobalListsLogEntry = "{0} : {1} from source collection [ {5} ] into target collection [ {6} ]";
        const String ErrorEntry = "Exception: {0}\n\n - While performing step: {1}";
        const String KeyAppConfigbackupFolder = "BACKUP_FOLDER";

        private Logging Logger = new Logging();
        Window toolWindow;
        TfsTeamProjectCollection SourceCollection;
        TfsTeamProjectCollection TargetCollection;
        WorkItemStore SourceStore;
        WorkItemStore TargetStore;        
        ProjectType SourceProjectType;
        
        private bool WITAdminFound;
        private bool isChecked;
        CheckedListItem<WorkItemType> CurrentItem;
        ArrayList WITsToExport;
        
        BackgroundWorker ImportWorker;
        ProjectCollection TargetProjectCollection;
        Project CurrentTargetProject;
        Project CurrentSourceProject;
        
        String BackupFolder = AppDomain.CurrentDomain.BaseDirectory;
        private QueryHierarchy QueryImporter;
        private QueryHierarchy HierarchySource;
        private String Folder;

        private TfsUtility Utility;
        private GlobalList GlobalListManager;
        private ImportProgress ProgressManager;
        private Iteration IterationManager;
       
        private WITLinkType LinkManager;
        private WorkItemCategory CategoryManager;
        private WorkItemTypeDef WorkItemTypeManager;

        private int _queryCount = 0;

        private int _selectedTargetsCount = 0;
        private int _selectedWITDCount = 0;
        Opaque hider;
        
        /// <summary>
        /// 
        /// </summary>
        public bool IsChecked
        {
            get
            {
                return isChecked;
            }
            set
            {
                isChecked = value;
                NotifyPropertyChanged("IsChecked");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void Init()
        {
            Utility = new TfsUtility();
            GlobalListManager = new GlobalList();
            IterationManager = new Iteration();
            LinkManager = new WITLinkType();
            
            WorkItemTypeManager = new WorkItemTypeDef();
            ProgressImport.Visibility = System.Windows.Visibility.Hidden;
            
            btnQuit.Visibility = System.Windows.Visibility.Visible;
            btnQuit.BringIntoView();
            ImportWorker = new BackgroundWorker();
            lblSourceCollection.TextChanged += this.CollectionChanged;
            lblTargetCollection.TextChanged += this.CollectionChanged;
            ImportWorker.DoWork += new DoWorkEventHandler(ImportWorker_DoWork);
            ImportWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ImportWorker_RunWorkerCompleted);
            ImportWorker.WorkerReportsProgress = true;
            ImportWorker.ProgressChanged += new ProgressChangedEventHandler(ImportWorker_ProgressChanged);
            ImportWorker.WorkerSupportsCancellation = true;
            
            DataContext = this;
            btnCategory.IsEnabled = false;
            btnManageWorkItems.IsEnabled = false;
            btnLinks.IsEnabled = false;
            btnSelectAllNone.IsEnabled = false;
            btnSelectAllProjects.IsEnabled = false;
            Utility = new TfsUtility();
            if (!Utility.WitAdminFound)
            {
                MessageBox.Show("Cannot find WitAdmin.exe. Work item types cannot be destroyed without it.");
                WITAdminFound = false;
            }
            else
            {
                WITAdminFound = true;
            }

            hider = new Opaque(this);
            

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="collection"></param>
        public void EnumVisual(Visual parent, List<Visual> collection) 
        {     

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
            {         // Get the child visual at specified index attributeValue.         
                Visual childVisual = (Visual)VisualTreeHelper.GetChild(parent, i);
                // Add the child visual object to the collection.         
                collection.Add(childVisual);          
                // Recursively enumerate children of the child visual object.         
                EnumVisual(childVisual, collection);     
            } 
        } 

        /// <summary>
        /// Reformats ToolTi[ps to smaller, more readable windows.
        /// </summary>
        private void SetToolTips()
        {
            Int32 width = (Int32)(System.Windows.SystemParameters.PrimaryScreenWidth / 3 / 10);
            List<Visual> controls = new List<Visual>();
            EnumVisual(this, controls);

            foreach (Visual ctrl in controls)
            {
                if (ctrl != null)
                {
                    if ((ctrl is CheckBox) || (ctrl is RadioButton) || (ctrl is TextBox) || (ctrl is ComboBox) || (ctrl is ListBox) || (ctrl is Button))
                    {
                        if ((ctrl as Control).ToolTip == null) { (ctrl as Control).ToolTip = ""; }
                        (ctrl as Control).ToolTip = FormatToolTip((ctrl as Control).ToolTip.ToString(), width);
                    }
                }
            }
        }

        /// <summary>
        /// Occurs after the window and all its controls have been instantiated.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            SetToolTips();
            
        }

        /// <summary>
        /// Reformats a String into one or more lines, denoted by CRLFs, no greater than the specified line length.
        /// </summary>
        /// <param name="text">The text to reformat.</param>
        /// <param name="maxLineLength">The maximum line length for the resulting text.</param>
        /// <returns>The reformatted text.</returns>
        private String FormatToolTip(String text, Int32 maxLineLength)
        {
            if (String.IsNullOrEmpty(text)) { return String.Empty;}
            if (maxLineLength < MinToolTipLength) { maxLineLength = MinToolTipLength; }

            StringBuilder resultText = new StringBuilder();
            String[] words = text.Split(new char[] {' ', ','});
            ArrayList lines = new ArrayList();
            String line = String.Empty;

            foreach (String word in words)
            {
                if (line.Length + word.Length >= maxLineLength)
                {
                    line += Environment.NewLine;
                    lines.Add(line);
                    line = String.Empty;
                }

                line += word + " ";
            }

            if (!String.IsNullOrEmpty(line)) { lines.Add(line); }
            foreach (String tipLine in lines)
            {
                resultText.Append(tipLine);
            }

            return resultText.ToString();

        }

        /// <summary>
        /// 
        /// </summary>
        public Window1()
            : base()
        {
            InitializeComponent();
            Init();
            
            SetupForStart();
        }

        /// <summary>
        /// 
        /// </summary>
        private void SetupForStart()
        {
            AllButtons(true);

            BackupFolder = WITD.Configuration.GetSavedBackupFolder(KeyAppConfigbackupFolder);
            if (!String.IsNullOrEmpty(BackupFolder))
            {
                txtBackupFolder.Text = BackupFolder;
            }
        }

        /// <summary>
        /// Creates a new query name with the current DateTime with illegal characters removed, with a custom String tag included.
        /// </summary>
        /// <param name="currentName">The current query name.</param>
        /// <param name="version">Usually used to identify which version of the query the new name will pertain to.</param>
        /// <returns>The new query name.</returns>
        private String GetNewName(String currentName, String version)
        {
            return (currentName + version + "-" +
                    DateTime.Now.ToShortDateString().Replace("/", "-").Replace(".", "-") + "-" +
                    DateTime.Now.ToLongTimeString().Replace(":", "-").Replace(".", "-"));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceStore"></param>
        /// <param name="targetStore"></param>
        /// <param name="progress"></param>
        private void UpdateLinks(WorkItemStore sourceStore, WorkItemStore targetStore, ImportProgress progress)
        {
            String message = null;
            bool success;
            progress.FormatString = UpdateLinkTypesLogEntry;

            ProgressUpdate(progress, "Updating link types...", null, null, null, null, false, false, false);

            if (LinkManager.LinkTypesDifferent(sourceStore, targetStore))
            {
                success = LinkManager.UpdateLinkTypes(sourceStore, targetStore, out message);

                if (String.IsNullOrEmpty(message))
                {
                    ProgressUpdate(progress, "Updating link types...success", null, null, null, null, true, false, false);
                }
                else
                {
                    ProgressUpdate(progress, "Error updating link types: " + message, null, null, null, null, true, true, false);
                }
            }
            else
            {
                ProgressUpdate(progress, "Link types are the same. Update is not necessary.", null, null, null, null, true, false, false);
            }
            
        }

        /// <summary>
        /// Updates the target collection's global lists with the source collection's global lists using the specified options.
        /// </summary>
        /// <param name="source">The WorkItemStore in which the source global list resides.</param>
        /// <param name="target">The WorkItemStore in which the target global list resides.</param>
        /// <param name="options">The options that govern whether to just add missing lists, make a copy of the lists, 
        /// or just add global lists to the work item types being exported.</param>
        /// <param name="progress"></param>
        private void UpdateGlobalLists(WorkItemStore source, WorkItemStore target, GlobalListCopyOptions options, ImportProgress progress)
        {
            String message;
            progress.FormatString = UpdateGlobalListsLogEntry;

            ProgressUpdate(progress, "Copying global lists...", null, null, null, null, false, false, false, 100);

            GlobalListManager.CopyGlobalLists(source, target, options, out message);

            if (String.IsNullOrEmpty(message))
            {
                ProgressUpdate(progress, "Copying global lists...success", null, null, null, null, true, false, false, 100);
            }
            else
            {
                ProgressUpdate(progress, "Error copying global lists: " + message, null, null, null, null, true, true, false, 100);
            }
        }

        /// <summary>
        /// Deletes all categories from the specified project. This allows WITDs to be deleted and readies a project to receive new Category definitions.
        /// </summary>
        /// <param name="sourceProject">The source project for instantiating the WorkItemCatehgory object.</param>
        /// <param name="targetProject">The project from which to delete the categories.</param>
        /// <param name="progress"></param>
        private void DeleteCategories(Project sourceProject, Project targetProject, ImportProgress progress)
        {
            String message = null;
            CategoryManager = new WorkItemCategory(sourceProject);
            progress.FormatString = CategoryDeleteLogEntry;

            ProgressUpdate(progress, "Deleting categories...", null, null, null, null, false, false, false, 100);

            CategoryManager.DeleteCategories(targetProject, out message);

            if (String.IsNullOrEmpty(message))
            {
                ProgressUpdate(progress, "Deleting categories...success", null, null, null, null, true, false, false, 100);
            }
            else
            {
                ProgressUpdate(progress, "Error deleting categories: " + message, null, null, null, null, true, true, false, 100);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceProject"></param>
        /// <param name="targetProject"></param>
        /// <param name="progress"></param>
        private void UpdateCategories(Project sourceProject, Project targetProject, ImportProgress progress)
        {
            String message = null;
            CategoryManager = new WorkItemCategory(sourceProject);
            progress.FormatString = CategoryImportLogEntry;

            ProgressUpdate(progress, "Updating categories...", null, null, null, null, false, false, false, 100);

            CategoryManager.UpdateCategories(sourceProject, targetProject, out message);

            if (String.IsNullOrEmpty(message))
            {
                ProgressUpdate(progress, "Updating categories...success", null, null, null, null, true, false, false, 100);
            }
            else
            {
                ProgressUpdate(progress, "Error updating categories: " + message, null, null, null, null, true, true, false, 100);
            }
        }

       /// <summary>
       /// 
       /// </summary>
       /// <param name="uri"></param>
       /// <param name="targetProject"></param>
       /// <param name="currentName"></param>
       /// <param name="newName"></param>
       /// <param name="progress"></param>
        private void RenameWITD(String uri, String targetProject, String currentName, String newName, ImportProgress progress)
        {
            String message = null;
            progress.FormatString = RenameWITLogEntry;

            ProgressUpdate(progress, "Renaming WITD...", null, null, null, null, false, false, false, 100);

            WorkItemTypeManager.RenameWITD(uri, targetProject, currentName, newName, out message);

            if (String.IsNullOrEmpty(message))
            {
                ProgressUpdate(progress, "Renaming WITD...success", null, null, null, null, true, false, false, 100);
            }
            else
            {
                ProgressUpdate(progress, "Error renaming WITD: " + message, null, null, null, null, true, true, false, 100);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="wit"></param>
        /// <param name="newName"></param>
        /// <param name="progress"></param>
        private XmlDocument RenameWITD(ImportableWIT wit, String newName, ImportProgress progress)
        {
            String message = null;
            XmlDocument xml = null;
            progress.FormatString = RenameWITLogEntry;

            ProgressUpdate(progress, "Renaming WITD...", null, null, null, null, false, false, false);

            xml = WorkItemTypeManager.RenameWITD(wit, newName, out message);

            if (String.IsNullOrEmpty(message))
            {
                ProgressUpdate(progress, "Renaming WITD...success", null, null, null, null, true, false, false);
            }
            else
            {
                ProgressUpdate(progress, "Error renaming WITD: " + message, null, null, null, null, true, true, false);
            }

            return xml;
        }

        /// <summary>
        /// This is where the background worker thread does all its work.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ImportWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            ArrayList witsToExport = new ArrayList();
            ArrayList targetProjects = new ArrayList();
            String SourceProject;

            GlobalListCopyOptions glob;
            IterationCopyOptions iter;
            WITDCopyOptions witd;
            QueryCopyOptions query;

            List<string> Results = new List<string>();
            CustomWork UserInputs = e.Argument as CustomWork;
            
            if (UserInputs != null)
            {
                glob = UserInputs.GlobalList;
                iter = UserInputs.Iterations;
                witd = UserInputs.WITDs;
                query = UserInputs.Queries;

                targetProjects = UserInputs.TargetProjects;
                SourceProject = UserInputs.SourceProject;
                witsToExport = UserInputs.WitsToImport;

                ProgressManager = new ImportProgress(UserInputs.NumberOfOperations);
                ProgressManager.UseLog = UserInputs.UseLog;
                ProgressManager.SourceProject = SourceProject;
                ProgressManager.FormatString = InitLogEntry;
                ProgressManager.SourceCollection = UserInputs.SourceCollection;
                ProgressManager.TargetCollection = UserInputs.TargetCollection;

                ProgressUpdate(ProgressManager, "Initializing ...", null, null, null, null, false);

                String newName;
                
                try
                {
                    if (!UserInputs.WITDs.ValidateOnly && UserInputs.GlobalList.IncludeGlobalList)
                    {
                        UpdateGlobalLists(UserInputs.SourceStore, UserInputs.TargetStore, UserInputs.GlobalList, ProgressManager);
                    }

                    if (ImportWorker.CancellationPending)
                    { e.Cancel = true; return; }

                    if (!UserInputs.WITDs.ValidateOnly && UserInputs.IncludeLinkTypes)
                    {
                        //for each linktype in Source collection, import it into target collection
                        UpdateLinks(UserInputs.SourceStore, UserInputs.TargetStore, ProgressManager);
                    }

                    foreach (String currentProject in targetProjects)
                    {
                        ProgressManager.CurrentProject = currentProject;

                        if (ImportWorker.CancellationPending)
                        { e.Cancel = true; return; }

                        if (!UserInputs.WITDs.ValidateOnly && UserInputs.Iterations.IncludeIterations)
                        {
                            //import the saved iterations into current target sourceProject
                            CopyIterations(UserInputs.SourceStore.TeamProjectCollection, 
                                           UserInputs.SourceStore.Projects[UserInputs.SourceProject], 
                                           UserInputs.TargetStore.TeamProjectCollection, 
                                           UserInputs.TargetStore.Projects[currentProject], 
                                           UserInputs.Iterations,
                                           ProgressManager);
                        }

                        if (ImportWorker.CancellationPending)
                        { e.Cancel = true; return; }

                        if (!UserInputs.WITDs.ValidateOnly && UserInputs.IncludeCategories)
                        {
                            //delete all existing categories
                            DeleteCategories(UserInputs.SourceStore.Projects[UserInputs.SourceProject], UserInputs.TargetStore.Projects[currentProject], ProgressManager);
                        }

                        if (ImportWorker.CancellationPending)
                        { e.Cancel = true; return; }

                        if ((UserInputs.WITDs.RemoveWITDs) && (!UserInputs.WITDs.ValidateOnly) && WITAdminFound)
                        {
                            DestroyUnimportedWITDs(UserInputs.TargetCollection, UserInputs.TargetStore, currentProject, ProgressManager);
                        }

                        if (UserInputs.WITDs.IncludeWITDs)
                        {

                            foreach (ImportableWIT item in WITsToExport)
                            {
                                if (ImportWorker.CancellationPending)
                                { e.Cancel = true; return; }
                                ProgressManager.FormatString = ValidationLogEntry;
                                ProgressUpdate(ProgressManager, "Validating", null, null, item.Name, null, false);

                                try
                                {
                                    WorkItemType.Validate(UserInputs.TargetStore.Projects[currentProject], item.Definition.InnerXml);
                                    ProgressUpdate(ProgressManager, "Successfully validated", null, null, null, null, true);
                                }

                                catch (XmlException ex)
                                {
                                    ProgressUpdate(ProgressManager, "Xml exception: " + ex.Message, null, null, null, ex, true, true);
                                    continue;
                                }
                                catch (XmlSchemaValidationException sex)
                                {
                                    ProgressUpdate(ProgressManager, "Xml Schema Validation Exception: \n\n" + sex.Message, null, null, null, sex, true, true);
                                    continue;
                                }

                                if (!UserInputs.WITDs.ValidateOnly)
                                {
                                    if (ImportWorker.CancellationPending)
                                    { e.Cancel = true; return; }
                                    try
                                    {
                                        ProgressManager.FormatString = WITLogEntry;
                                        if ((UserInputs.TargetStore.Projects[currentProject].WorkItemTypes.Contains(item.Name)))
                                        {

                                            switch (UserInputs.WITDs.Options)
                                            {
                                                case WITDOptions.Overwrite:
                                                    ProgressUpdate(ProgressManager, "Importing and overwriting", null, null, null, null, false);
                                                    UserInputs.TargetStore.Projects[currentProject].WorkItemTypes.Import(item.Definition.InnerXml);
                                                    break;

                                                case WITDOptions.RenameNew:
                                                    ProgressUpdate(ProgressManager, "Importing, renaming new wit", null, null, null, null, false);
                                                    newName = GetNewName(item.Name, "_new_");
                                                    item.Name = newName;
                                                    item.Definition = RenameWITD(item, newName, ProgressManager);
                                                    UserInputs.TargetStore.Projects[currentProject].WorkItemTypes.Import(item.Definition.InnerXml);
                                                    break;

                                                case WITDOptions.RenameOld:
                                                    ProgressManager.FormatString = WITLogEntry;
                                                    ProgressUpdate(ProgressManager, "Importing, renaming old wit", null, null, null, null, false);
                                                    newName = GetNewName(item.Name, "_old_");
                                                    RenameWITD(UserInputs.TargetStore.TeamProjectCollection.Uri.ToString(), currentProject, item.Name, newName, ProgressManager);
                                                    UserInputs.TargetStore.Projects[currentProject].WorkItemTypes.Import(item.Definition.InnerXml);
                                                    break;

                                                case WITDOptions.Skip:
                                                    ProgressUpdate(ProgressManager, "Skipping wit import", null, null, null, null, false);
                                                    break;
                                            }
                                        }
                                        else
                                        {
                                            ProgressUpdate(ProgressManager, "Importing", null, null, null, null, false);
                                            UserInputs.TargetStore.Projects[currentProject].WorkItemTypes.Import(item.Definition.InnerXml);
                                        }
                                        ProgressManager.FormatString = WITLogEntry;
                                        ProgressUpdate(ProgressManager, ProgressManager.StatusText + "...success", null, null, null, null, true);

                                    }
                                    catch (Exception sex)
                                    {
                                        ProgressUpdate(ProgressManager, "Error importing " + item.Name + " into project " + currentProject + ": \n\n" + sex.Message, null, null, null, null, true, true);
                                        continue;
                                    }
                                }
                            }
                        }

                        if (ImportWorker.CancellationPending)
                        { e.Cancel = true; return; }

                        if (!UserInputs.WITDs.ValidateOnly && UserInputs.IncludeCategories)
                        {
                            //get categories from source project into current target project
                            UpdateCategories(UserInputs.SourceStore.Projects[UserInputs.SourceProject], UserInputs.TargetStore.Projects[currentProject], ProgressManager);
                        }

                        if (ImportWorker.CancellationPending)
                        { e.Cancel = true; return; }

                        //now do queries
                        if (!UserInputs.WITDs.ValidateOnly && UserInputs.Queries.IncludeQueries)
                        {
                            //never remove all queries if skipping the overwrite
                            if (UserInputs.Queries.Options != QueryOptions.Skip) { DeleteQueries(UserInputs.TargetStore.Projects[currentProject], ProgressManager); }
                            CopyQueries(UserInputs.SourceStore.Projects[UserInputs.SourceProject], UserInputs.TargetStore.Projects[currentProject], UserInputs.Queries, ProgressManager);
                        }

                        if (ImportWorker.CancellationPending)
                        { e.Cancel = true; return; }

                        //work items
                        if (!UserInputs.WITDs.ValidateOnly && UserInputs.TargetProjects.Count == 1 && UserInputs.WITDs.IncludeWorkItems)
                        {
                            WorkItemTypeDef witManager = new WorkItemTypeDef();

                            int failures = witManager.CopyWorkItemsBetweenProjects(UserInputs.SourceStore.Projects[UserInputs.SourceProject],
                                                                                    UserInputs.TargetStore.Projects[currentProject],
                                                                                    UserInputs.SourceStore,
                                                                                    UserInputs.TargetStore,
                                                                                    UserInputs.WitsToImport);
                            if (failures > 0)
                                ProgressUpdate(ProgressManager, failures.ToString() + " failures occurred while copying work items.", null, null, null, null, true, false, true);
                        }
                    }

                    ProgressUpdate(ProgressManager, "Done", "", "", "", null, true, false, true);
                }

                catch (WorkItemTypeDeniedOrNotExistException ex)
                {
                    ProgressUpdate(ProgressManager, "Error: " + ex.Message, null, null, null, ex, true, true, true, 100);
                    MessageBox.Show(ex.Message);
                }
            }
        }

        /// <summary>
        /// Where the background worker thread ends.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ImportWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Cleanup();
            Reconnect(SourceStore.Projects[SourceProjectType.Name], -1);
        }
      
        /// <summary>
        /// Where progress is reported to the UI.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ImportWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            ImportProgress currentProgress = e.UserState as ImportProgress;
            
            ProgressImport.Value = currentProgress.PercentComplete;

            if (currentProgress.UseLog)
            {
                Logger.Log(currentProgress);
            }

            if (!String.IsNullOrEmpty(currentProgress.StatusText)) { lblStatusText.Content = currentProgress.StatusText; }
            this.txtSourceProject.Text = currentProgress.SourceProject;
            this.txtCurrentProject.Text = currentProgress.CurrentProject;
            this.txtCurrentWIT.Text = currentProgress.CurrentWIT;

        }

        /// <summary>
        /// Copies project iterations from a source project to a target project using the specified copy option settings.
        /// </summary>
        /// <param name="sourceCollection"></param>
        /// <param name="sourceProject"></param>
        /// <param name="targetCollection"></param>
        /// <param name="targetProject"></param>
        /// <param name="options"></param>
        /// <param name="progress"></param>
        private void CopyIterations(TfsTeamProjectCollection sourceCollection, 
                                    Project sourceProject, 
                                    TfsTeamProjectCollection targetCollection, 
                                    Project targetProject, 
                                    IterationCopyOptions options,
                                    ImportProgress progress)
        {
            ProgressManager.FormatString = IterationExportLogEntry;
            ProgressUpdate(progress, "Exporting iteration nodes...", null, null, null, null, false);
            MemoryStream ms = IterationManager.ExportIterationNodes(sourceCollection, sourceProject);
            ProgressUpdate(progress, "Exporting iteration nodes...successful", null, null, null, null, true);

            ProgressManager.FormatString = IterationImportLogEntry;
            ProgressUpdate(progress, "Importing iteration nodes...", null, null, null, null, false);
            IterationManager.ImportIterationNodes(targetCollection, ms, targetProject, options);
            ProgressUpdate(progress, "Importing iteration nodes...successful", null, null, null, null, true);
        }

        /// <summary>
        /// Handles the connection to one of the available registered Team Project Collections. 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSourceCollection_Click(object sender, RoutedEventArgs e)
        {
            TeamProjectPicker dp = new TeamProjectPicker(TeamProjectPickerMode.NoProject, false);
            ObscureMainWindow(null);
            dp.ShowDialog();
            
            try
            {
                if (dp.SelectedTeamProjectCollection != null)
                {
                    SourceCollection = new TfsTeamProjectCollection(dp.SelectedTeamProjectCollection.Uri);
                    SourceCollection.EnsureAuthenticated();
                    lblSourceCollection.Text = SourceCollection.Uri.ToString();
                    SourceStore = (WorkItemStore)SourceCollection.GetService(typeof(WorkItemStore));
                    GetProject();
                    CurrentSourceProject = null;
                    lblSourceProcesstemplate.Text = "";
                    ExecutionEnable(false);
                    btnLinks.IsEnabled = true;
                    btnCategory.IsEnabled = false;
                    btnManageWorkItems.IsEnabled = false;
                    btnSelectAllNone.IsEnabled = false;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                UnobscureMainWindow();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="enabled"></param>
        private void ExecutionEnable(bool enabled)
        {
            btnExport.IsEnabled = enabled;
            lblSelectedTargetCount.Content = "Selected target projects count: " + _selectedTargetsCount.ToString();
            lblSelectedWITDCount.Content = "Selected WITD count: " + _selectedWITDCount.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        private void GetProject()
        {
            cob_projects.Items.Clear();
            foreach (Project project in SourceStore.Projects)
            {
                cob_projects.Items.Add(new ProjectType(project));
            }
            cob_projects.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", System.ComponentModel.ListSortDirection.Ascending));
        }

        /// <summary>
        /// Retrieves the process template details for the selected source project. 
        /// </summary>
        /// <returns>The process template details for the selected source project.</returns>
        private ProjectProperty[] GetProcessTemplateDetailsForTheSelectedProject()
        {
            var vcs = SourceCollection.GetService<VersionControlServer>();
            var ics = SourceCollection.GetService<ICommonStructureService>();
            ProjectProperty[] ProjectProperties = null;

            var p = vcs.GetTeamProject(CurrentSourceProject.Name);
            string ProjectName = string.Empty;
            string ProjectState = String.Empty;
            int templateId = 0;
            ProjectProperties = null;
            ics.ClearProjectInfoCache();
            ics.GetProjectProperties(p.ArtifactUri.AbsoluteUri, out ProjectName, out ProjectState, out templateId, out ProjectProperties);

            return ProjectProperties;
        }

        /// <summary>
        /// Determines if source and at least one target project is selected.
        /// </summary>
        /// <returns>True if source and at least one target project is selected. Otherwise false.</returns>
        private bool ProjectsSelected()
        {
            if (cob_projects.SelectedItem != null && TargetProjectsChecked())
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Determines if at least one WIT is selected.
        /// </summary>
        /// <returns>True if at least one WIT is selected. Otherwise false.</returns>
        private bool WITsSelected()
        {
            if (SourceSelected())
            {
                foreach (CheckedListItem<WorkItemType> item in cob_wittype.Items)
                {
                    if (item.IsChecked)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="projects"></param>
        private void GetProjects(ProjectCollection projects)
        {
            cboTargetProjects.Items.Clear();
            foreach (Project targetProject in projects)
            {
                //do not add selected source sourceProject to the target sourceProject list
                if (!Utility.CollectionsDifferent(SourceCollection, TargetCollection) && SourceProjectType != null && targetProject.Name == SourceProjectType.Name) { continue; }

                ProjectType item = new ProjectType(targetProject);
                CheckedListItem<ProjectType> newerItem = new CheckedListItem<ProjectType>(item);
                cboTargetProjects.Items.Add(newerItem);
            }

            cboTargetProjects.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("Item.Name", System.ComponentModel.ListSortDirection.Ascending));
            btnSelectAllProjects.Content = SelectAllProjectsCaption;
        }

       

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Chk_Checked(object sender, RoutedEventArgs e)
        {
            bool hasCheckedItems = false;
            if (!cob_wittype.HasItems)
            {
                btnSelectAllNone.IsEnabled = false;
                ExecutionEnable(false);
             }
            else
            {
                _selectedWITDCount++;
                btnSelectAllNone.IsEnabled = true;
                foreach (CheckedListItem<WorkItemType> item in cob_wittype.Items)
                {
                    if (item.IsChecked)
                    {
                        hasCheckedItems = true;
                        break;
                    }
                }
                ExecutionEnable(hasCheckedItems && TargetsSelected());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Chk_Unchecked(object sender, RoutedEventArgs e)
        {
            bool hasCheckedItems = false;
            if (!cob_wittype.HasItems)
            {
                btnSelectAllNone.IsEnabled = false;
                ExecutionEnable(false);
            }
            else
            {
                _selectedWITDCount--;
                btnSelectAllNone.IsEnabled = true;
                foreach (CheckedListItem<WorkItemType> item in cob_wittype.Items)
                {
                    if (item.IsChecked)
                    {
                        hasCheckedItems = true;
                        break;
                    }
                }
                ExecutionEnable(hasCheckedItems && TargetsSelected());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Chk_TargetsChecked(object sender, RoutedEventArgs e)
        {
            bool hasCheckedItems = false;
            if (!cboTargetProjects.HasItems)
            {
                btnSelectAllProjects.IsEnabled = false;
                ExecutionEnable(false);
            }
            else
            {
                _selectedTargetsCount++;
                btnSelectAllProjects.IsEnabled = true;
                foreach (CheckedListItem<ProjectType> item in cboTargetProjects.Items)
                {
                    if (item.IsChecked)
                    {
                        hasCheckedItems = true;
                        break;
                    }
                }

                ExecutionEnable(hasCheckedItems && SourceSelected());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Chk_TargetsUnchecked(object sender, RoutedEventArgs e)
        {
            bool hasCheckedItems = false;
            if (!cboTargetProjects.HasItems)
            {
                btnSelectAllProjects.IsEnabled = false;
                ExecutionEnable(false);
            }
            else
            {
                _selectedTargetsCount--;
                btnSelectAllProjects.IsEnabled = true;
                foreach (CheckedListItem<ProjectType> item in cboTargetProjects.Items)
                {
                    if (item.IsChecked)
                    {
                        hasCheckedItems = true;
                        break;
                    }
                }

                ExecutionEnable(hasCheckedItems && SourceSelected());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="project"></param>
        private void PopulateWITDs(ProjectType project)
        {
            cob_wittype.Items.Clear();
            //SourceWorkItems.Clear();
            foreach (WorkItemType wittype in project.WitTypeCollection)
            {
                CheckedListItem<WorkItemType> newItem = new CheckedListItem<WorkItemType>(wittype, true);
                //SourceWorkItems.Add(newItem);
                cob_wittype.Items.Add(newItem);
            }

            cob_wittype.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("Item.Name", System.ComponentModel.ListSortDirection.Ascending));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cob_projects_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cob_wittype.Items.Count > 0) { cob_wittype.Items.Clear(); }
            if (cob_projects.SelectedItem != null)
            {
                SourceProjectType = cob_projects.SelectedItem as ProjectType;
                btnSelectAllNone.IsEnabled = true;
                CurrentSourceProject = SourceStore.Projects[SourceProjectType.Name];
                var processTemplateDetails = GetProcessTemplateDetailsForTheSelectedProject();

                var processTemplateName = processTemplateDetails.Where(pt => pt.Name == "templateName").Select(pt => pt.Value).FirstOrDefault();
                lblSourceProcesstemplate.Text = processTemplateName;
                this.txtSourceProject.Text = SourceProjectType.Name;
                cob_wittype.Focus();
                
                foreach (WorkItemType wittype in (cob_projects.SelectedItem as ProjectType).WitTypeCollection)
                {
                    CheckedListItem<WorkItemType> newItem = new CheckedListItem<WorkItemType>(wittype, true);
                    cob_wittype.Items.Add(newItem);
                }
                btnSelectAllNone.Content = SelectNoWITDsCaption;
                cob_wittype.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("Item.Name", System.ComponentModel.ListSortDirection.Ascending));

                if (TargetStore != null) { GetProjects(TargetStore.Projects); }
            }
            _selectedWITDCount = cob_wittype.Items.Count;
            ExecutionEnable(SourceSelected() && TargetsSelected());
            btnCategory.IsEnabled = true;
            btnManageWorkItems.IsEnabled = true;
            btnSelectAllNone.IsEnabled = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cob_wittype_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cob_wittype.SelectedItem != null)
            {
                if ((cob_wittype.SelectedItem as CheckedListItem<WorkItemType>).IsChecked)
                {
                    (cob_wittype.SelectedItem as CheckedListItem<WorkItemType>).IsChecked = false;
                }
                else
                {
                    (cob_wittype.SelectedItem as CheckedListItem<WorkItemType>).IsChecked = true;
                }
                btnSelectAllNone_Click(sender, e);
            }

            ExecutionEnable(SourceSelected() && TargetsSelected());
        }

        /// <summary>
        /// Collects and stores the user's selections and execution options. Also calculates the number of separate reportable operations to move the progress bar correctly.
        /// </summary>
        /// <returns>A CustomWork object populated with the user's selections and execution options.</returns>
        private CustomWork CollectUIData()
        {
            int numOps = 0;
            WITsToExport = new ArrayList();

            CustomWork newImportData;
            ArrayList targets = new ArrayList();

            if (chkIncludeWITDs.IsChecked.Value)
            {
                foreach (CheckedListItem<WorkItemType> wit in cob_wittype.Items)
                {
                    if (wit.IsChecked)
                    {
                        XmlDocument definition = this.SourceStore.Projects[SourceProjectType.Name].WorkItemTypes[wit.Item.Name].Export(this.global.IsChecked.Value);
                        ImportableWIT item = new ImportableWIT(wit.Item.Name, definition);
                        WITsToExport.Add(item);
                    }
                    else
                    {
                        //only allow witd removal when ALL source witds are being copied
                        chkRemoveWITDs.IsChecked = false;
                    }
                }
            }
            
            foreach (CheckedListItem<ProjectType> currentProject in cboTargetProjects.Items)
            {
                if (currentProject.IsChecked)
                {
                    targets.Add(currentProject.Item.Name);
                }
            }

            int count = 0;
            if (!chkValidateOnly.IsChecked.Value && chkRemoveWITDs.IsChecked.Value) { count = CountWITDsToDestroy(TargetStore, targets); }
            if (!chkValidateOnly.IsChecked.Value)
            {
                numOps = ((WITsToExport.Count * targets.Count) * 2) + count;
            }
            else
            {
                numOps = (WITsToExport.Count * targets.Count) + count;
            }

            GlobalListCopyOptions glOptions = new GlobalListCopyOptions();
            glOptions.IncludeGlobalList = global.IsChecked.Value;
            if (!chkValidateOnly.IsChecked.Value && glOptions.IncludeGlobalList)
            {
               
                if (rbAddToWITDOnly.IsChecked.Value)
                {
                    glOptions.Options = GlobalListOptions.AddToWITDOnly;
                }
                else if (rbAddMissingGlobalLists.IsChecked.Value)
                {
                    glOptions.Options = GlobalListOptions.AddMissing;
                    numOps += 1;
                }
                else
                    {
                        glOptions.Options = GlobalListOptions.OverwriteAll;
                        numOps += 1;
                    }
            }

            QueryCopyOptions qryOptions = new QueryCopyOptions();
            qryOptions.IncludeQueries = chkIncludeQueries.IsChecked.Value;
            if (!chkValidateOnly.IsChecked.Value && qryOptions.IncludeQueries) 
            {
                _queryCount = 0;
                numOps += (QueryCount(SourceStore.Projects[SourceProjectType.Name]) * targets.Count);
                //skip and remove are not compatible
                qryOptions.RemoveQueries = !rbSkipQuery.IsChecked.Value && chkRemoveQueries.IsChecked.Value;
                if (qryOptions.RemoveQueries) { numOps += targets.Count; }
                if (rbOverwriteQuery.IsChecked.Value)
                {
                    qryOptions.Options = QueryOptions.Overwrite;
                }
                else if (rbRenameNewQuery.IsChecked.Value)
                     {
                         qryOptions.Options = QueryOptions.RenameNew;
                     }
                     else if (rbRenameOldQuery.IsChecked.Value)
                          {
                              qryOptions.Options = QueryOptions.RenameOld;
                          }
                          else
                          {
                              qryOptions.Options = QueryOptions.Skip;
                          }
            }
            
            IterationCopyOptions iterOptions = new IterationCopyOptions();
            iterOptions.IncludeIterations = chkIncludeIterations.IsChecked.Value;
            if (!chkValidateOnly.IsChecked.Value && iterOptions.IncludeIterations) 
            { 
                numOps += (2 * targets.Count); 
                if (rbAddMissingIterations.IsChecked.Value)
                {
                    iterOptions.Options = IterationOptions.AddMissing;
                }
                else
                {
                    iterOptions.Options = IterationOptions.OverwriteAll;
                }
            }

            WITDCopyOptions witCopyOptions = new WITDCopyOptions();

            if (!chkValidateOnly.IsChecked.Value && chkIncludeWITDs.IsChecked.Value)
            {
                witCopyOptions.IncludeWITDs = chkIncludeWITDs.IsChecked.Value;
                if (rbOverwriteWIT.IsChecked.Value)
                {
                    witCopyOptions.Options = WITDOptions.Overwrite;
                }
                else if (rbRenameNewWIT.IsChecked.Value)
                     {
                         witCopyOptions.Options = WITDOptions.RenameNew;
                     }
                     else if (rbRenameOldWIT.IsChecked.Value)
                          {
                              witCopyOptions.Options = WITDOptions.RenameOld;
                          }
                          else
                          {
                              witCopyOptions.Options = WITDOptions.Skip;
                          }
                witCopyOptions.RemoveWITDs = chkRemoveWITDs.IsChecked.Value;
                witCopyOptions.SetAllowRemoval(chkRemoveWITDs.IsChecked.Value);
                witCopyOptions.IncludeWorkItems = chkCopyWorkItems.IsChecked.Value;
            }

            if (!chkValidateOnly.IsChecked.Value && chkIncludeCategories.IsChecked.Value) { numOps += (2 * targets.Count); }

            ProgressImport.Maximum = Convert.ToDouble(numOps);
                        
            newImportData = new CustomWork(chkIncludeCategories.IsChecked.Value,
                                           chkIncludeLinks.IsChecked.Value,
                                           chklLog.IsChecked.Value,
                                           qryOptions,
                                           iterOptions,
                                           witCopyOptions,
                                           glOptions,
                                           SourceProjectType.Name,
                                           targets,
                                           WITsToExport,
                                           SourceStore,
                                           TargetStore,
                                           SourceCollection.Uri.ToString(),
                                           TargetCollection.Uri.ToString(),
                                           numOps);

            return newImportData;
        }

        /// <summary>
        /// Executes the user's selections and options.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExport_Click(object sender, RoutedEventArgs e)
        {
            if (cob_projects.SelectedItem != null)
            {
                StartUp();

                CustomWork data = CollectUIData();
                LogUserSelections(data);
                ImportWorker.RunWorkerAsync(data);
            }
            else
            {
                MessageBox.Show("Please select a source team project.");
            }
        }

        /// <summary>
        /// Logs the selections made by the user to the beginning of the log file.
        /// </summary>
        /// <param name="data">A CustomWork object populated with the user's selections and execution options.</param>
        private void LogUserSelections(CustomWork data)
        {
            Logger.Add("Validate only: " + data.WITDs.ValidateOnly.ToString());
            Logger.Add("Include queries: " + data.Queries.IncludeQueries.ToString());
            if (data.Queries.IncludeQueries)
            {
                Logger.Add("Query option: " + data.Queries.Options.ToString());
            }
            Logger.Add("Include iterations: " + data.Iterations.IncludeIterations.ToString());
            if (data.Iterations.IncludeIterations)
            {
                Logger.Add("Iteration option: " + data.Iterations.Options.ToString());
            }
            Logger.Add("Include WITDs: " + data.WITDs.IncludeWITDs.ToString());
            if (data.WITDs.IncludeWITDs)
            {
                Logger.Add("WITD option: " + data.WITDs.Options.ToString());
                Logger.Add("Remove other WITDs: " + data.WITDs.RemoveWITDs.ToString());
            }
            Logger.Add("Include Global List: " + data.GlobalList.IncludeGlobalList.ToString());
            if (data.GlobalList.IncludeGlobalList)
            {
                Logger.Add("Global list option: " + data.GlobalList.IncludeGlobalList.ToString());
            }
            Logger.Add("Include categories: " + data.IncludeCategories.ToString());
            Logger.Add("Include link types: " + data.IncludeLinkTypes.ToString());
            Logger.Add("Source collection: " + data.SourceCollection.ToString());
            Logger.Add("Source project: " + data.SourceProject.ToString() + Environment.NewLine);
            Logger.Add("Target collection: " + data.TargetCollection.ToString());
            Logger.Add("Target projects: " + Environment.NewLine);

            foreach (String project in data.TargetProjects)
            {
                Logger.Add(project);
            }

            Logger.Add(Environment.NewLine);

            Logger.Add("WITDs to export to target projects: " + Environment.NewLine);

            foreach (ImportableWIT wit in data.WitsToImport)
            {
                Logger.Add(wit.Name);
            }

            Logger.Add(Environment.NewLine);
        }

        /// <summary>
        /// Destroys the WITDs in a specified project that exist in it but because a new set of WITDs are 
        /// being imported, the extraneous WITDs are unwanted.
        /// </summary>
        /// <param name="targetUri"></param>
        /// <param name="store"></param>
        /// <param name="projectName"></param>
        /// <param name="progress"></param>
        private void DestroyUnimportedWITDs(String targetUri, WorkItemStore store, String projectName, ImportProgress progress)
        {
            ArrayList witdsToDestroy = GetWITDsNotImported(store, projectName);
            progress.FormatString = DestroyWITLogEntry;

            foreach (String wit in witdsToDestroy)
            {
                try
                {
                    ProgressUpdate(progress, "Destroying work item type...", null, null, wit, null, false);
                    DestroyWITD(targetUri, projectName, wit, progress);
                    ProgressUpdate(progress, "Successfully destroyed work item type...", null, null, wit, null, true);
                }

                catch (Exception ex)
                {
                    ProgressUpdate(progress, "Error destroying work item type: " + ex.Message, null, null, null, ex, false, true);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="store"></param>
        /// <param name="targetProjects"></param>
        /// <returns></returns>
        private int CountWITDsToDestroy(WorkItemStore store, ArrayList targetProjects)
        {
            int count = 0;
            foreach (String project in targetProjects)
            {
                count += GetWITDsNotImported(store, project).Count;
            }

            return count;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="wiStore"></param>
        /// <param name="projectName"></param>
        /// <returns></returns>
        private ArrayList GetWITDsNotImported(WorkItemStore wiStore, String projectName)
        {
            ArrayList witds = new ArrayList();
            if (WITsToExport != null)
            {
                foreach (WorkItemType wit in wiStore.Projects[projectName].WorkItemTypes)
                {
                    if (!WITInList(wit.Name))
                    {
                        witds.Add(wit.Name);
                    }
                }
            }
            return witds;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private bool WITInList(String name)
        {
            foreach (ImportableWIT item in WITsToExport)
            {
                if (item.Name == name)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        private bool CategoriesDifferent(Project source, Project target)
        {
            foreach (Category cat in source.Categories)
            {
                if (!target.Categories.Contains(cat.Name))
                {
                    return true;
                }
            }

            foreach (Category cat in target.Categories)
            {
                if (!source.Categories.Contains(cat.Name))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="projectName"></param>
        /// <param name="witName"></param>
        /// <param name="progress"></param>
        private void DestroyWITD(String uri, String projectName, String witName, ImportProgress progress)
        {
            int exitCode;

            try
            {
                CurrentTargetProject = TargetProjectCollection[projectName];
                WorkItemType wit = CurrentTargetProject.WorkItemTypes[witName];
                WorkItemCategory catMgr = new WorkItemCategory(CurrentTargetProject);
                
                String commandLine = String.Format("destroywitd /collection:{0} /p:\"{1}\" /n:\"{2}\" /noprompt", uri, projectName, witName);

                exitCode = Utility.RunProcess(Utility.WitCommandName, commandLine);
               
            }
            catch (Exception ex)
            {
                //log error
                ProgressUpdate(progress, "Error destroying " + witName + " in project " + projectName + ": " + ex.Message, null, projectName, witName, ex, false, true, false);
            }
        }


     

        /// <summary>
        /// Sets up the user interface for execution of selected actions by hiding the Quit button, showing a Cancel button, showing the progress bar, <para></para>
        /// and creating a new log file if logging is selected.
        /// </summary>
        private void StartUp()
        {
            SetCursor(System.Windows.Input.Cursors.Wait);
            btnQuit.Visibility = System.Windows.Visibility.Hidden;
            btnCancel.Visibility = Visibility.Visible;
            btnCancel.BringIntoView();
            btnCancel.Focus();
            btnLinks.IsEnabled = false;
            btnCategory.IsEnabled = false;
            btnManageWorkItems.IsEnabled = false;

            ProgressImport.Visibility = Visibility.Visible;
            
            lblStatus.Visibility = Visibility.Visible;

            if (chklLog.IsChecked.Value)
            {
                Logger.CreateLogFile();
            }
        }

        /// <summary>
        /// Unchecks all work item types in the source list.
        /// </summary>
        private void DeselectSourceWITs()
        {
            btnSelectAllNone.Content = SelectAllCaption;
            btnSelectAllNone.IsEnabled = false;

            foreach (CheckedListItem<WorkItemType> item in cob_wittype.Items)
            {
                item.IsChecked = false;
            }
            btnSelectAllNone.IsEnabled = true;
        }

        /// <summary>
        /// Unchecks all projects in the target projects list.
        /// </summary>
        private void DeselectTargetProjects()
        {

            foreach (CheckedListItem<ProjectType> item in cboTargetProjects.Items)
            {
                item.IsChecked = false;
            }
            lblSelectedTargetCount.Content = 0;
        }

        /// <summary>
        /// Sets the form back to its starting state, flushes and closes the log file if used, and empties the status textboxes.
        /// </summary>
        private void Cleanup()
        {
            try
            {
                Logger.CleanupLog();

                txtCurrentProject.Text = "";
                txtCurrentWIT.Text = "";
                txtSourceProject.Text = "";

                ProgressImport.Visibility = System.Windows.Visibility.Hidden;
                lblStatus.Visibility = Visibility.Hidden;
                DeselectSourceWITs();
                DeselectTargetProjects();
            }

            finally
            {
                AllButtons(true);
                SetCursor(System.Windows.Input.Cursors.Arrow);
            }
        }

        

        /// <summary>
        /// Sets the mouse cursor to the specified cursor.
        /// </summary>
        /// <param name="newCursor">The new cursor.</param>
        public void SetCursor(Cursor newCursor)
        {
            this.Cursor = newCursor;
        }

        /// <summary>
        /// Either checks all work item types or unchecks all work item types in the source list, depending on the current state of the button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelectAllNone_Click(object sender, RoutedEventArgs e)
        {
            bool doCheck;
            String msg;

            if (!cob_projects.HasItems)
            {
                _selectedWITDCount = 0;
                ExecutionEnable(false);
            }

            if ((btnSelectAllNone.Content as string) == SelectAllCaption)
            {

                doCheck = true;
                msg = SelectNoWITDsCaption;
                _selectedWITDCount = cob_wittype.Items.Count;
                
            }
            else
            {
                doCheck = false;
                msg = SelectAllCaption;
                _selectedWITDCount = 0;
            }

            ManageCheckedWITDHandlers(false, cob_wittype, "chkChecked", Chk_Checked, Chk_Unchecked);
            btnSelectAllNone.Content = msg;

            foreach (CheckedListItem<WorkItemType> item in cob_wittype.Items)
            {
                item.IsChecked = doCheck;
                CurrentItem = item;
            }
            ManageCheckedWITDHandlers(true, cob_wittype, "chkChecked", Chk_Checked, Chk_Unchecked);
            ExecutionEnable(doCheck && TargetsSelected());
        }

        /// <summary>
        /// Occurs in response to the Select Target Collection button Click event.<para></para><para></para>
        /// First, the Project Collection selection dialog is displayed. Once selected and OK is clicked, <para></para>
        /// the target collection is connected and authenticated with the current user credentials.<para></para>
        /// The URI of the target collection is set, the <see cref="WorkItemStore">target WorkItemStore</see> object is retrieved as well as all the <para></para>
        /// projects in it.<para></para>
        /// Then, the list of target projects is populated in the user interface, and, if projects do exist, the Select all projects button is enabled.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnTargetCollection_Click(object sender, RoutedEventArgs e)
        {
            TeamProjectPicker dp = new TeamProjectPicker(TeamProjectPickerMode.NoProject, false);
            ObscureMainWindow(null);
            dp.ShowDialog();

            try
            {
                if (dp.SelectedTeamProjectCollection != null)
                {
                    TargetCollection = new TfsTeamProjectCollection(dp.SelectedTeamProjectCollection.Uri);
                    TargetCollection.EnsureAuthenticated();
                    lblTargetCollection.Text = TargetCollection.Uri.ToString();
                    TargetStore = (WorkItemStore)TargetCollection.GetService(typeof(WorkItemStore));
                    TargetProjectCollection = TargetStore.Projects;
                    GetProjects(TargetStore.Projects);
                    if (cob_projects.HasItems) { btnSelectAllProjects.IsEnabled = true; }
                    ExecutionEnable(false);
                    btnSelectAllProjects.IsEnabled = true;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                UnobscureMainWindow();
            }
        }

        /// <summary>
        /// Event handler for when the source or target project collections are selected.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CollectionChanged(Object sender, EventArgs e)
        {
            if (CollectionsSelected())
            {
                SetControlsForCollectionsSelected(lblSourceCollection.Text != lblTargetCollection.Text);
            }
            else
            {
                ExecutionEnable(false);
            }
        }

        /// <summary>
        /// Sets various controls based on whether the selected project collections are the same or different.
        /// <para></para><para></para>
        /// If the selected project collections are different, then the following occurs:<para></para>
        /// - The Include global list checkbox is enabled<para></para>
        /// - The Include link types checkbox is enabled<para></para>
        /// - The colors of the project collection URIs are changed to different colors to aid visually<para></para><para></para>
        /// If the selected project collections are the same, then<para></para>
        /// - The Include global list checkbox is disabled<para></para>
        /// - The Include link types checkbox is disabled<para></para>
        /// - The colors of the project collection URIs are changed to the same color<para></para><para></para>
        /// In any case, once both project collections are selected, the Execute selected actions button is enabled.
        /// </summary>
        /// <param name="collectionsDifferent">True if the selected project collections are different.</param>
        private void SetControlsForCollectionsSelected(bool collectionsDifferent)
        {
            global.IsEnabled = collectionsDifferent;
            rbAddMissingGlobalLists.IsEnabled = global.IsEnabled;
            rbOverwriteAllGlobalLists.IsEnabled = global.IsEnabled;
            rbAddToWITDOnly.IsEnabled = global.IsEnabled;

            if (!global.IsEnabled)
            {
                global.IsChecked = collectionsDifferent;
                rbAddMissingGlobalLists.IsChecked = global.IsEnabled;
                rbOverwriteAllGlobalLists.IsChecked = global.IsEnabled;
            }

            chkIncludeLinks.IsEnabled = collectionsDifferent;
            if (!chkIncludeLinks.IsEnabled) { chkIncludeLinks.IsChecked = collectionsDifferent; }

            ExecutionEnable(SourceSelected() && TargetsSelected());

            if (collectionsDifferent)
            {
                lblSourceCollection.Background = new SolidColorBrush(Colors.PowderBlue);
                lblTargetCollection.Background = new SolidColorBrush(Colors.LightSalmon);
                lblCollectionsWarning.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                lblSourceCollection.Background = new SolidColorBrush(Colors.LightSalmon);
                lblTargetCollection.Background = new SolidColorBrush(Colors.LightSalmon);
                lblCollectionsWarning.Visibility = System.Windows.Visibility.Hidden;

            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private bool TargetsSelected()
        {
            bool hasCheckedItems = false;

            if (!cboTargetProjects.HasItems)
            {
                return false;
            }
            else
            {
                foreach (CheckedListItem<ProjectType> item in cboTargetProjects.Items)
                {
                    if (item.IsChecked)
                    {
                        hasCheckedItems = true;
                        break;
                    }
                }
            }
            return hasCheckedItems;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private bool SourceSelected()
        {
            //check for a source project and work item types
            bool hasCheckedItems = false;

            if (!cob_projects.HasItems)
            {
                //btnLinks.IsEnabled = true;
                return false;
            }
            else
            {
                foreach (CheckedListItem<WorkItemType> item in cob_wittype.Items)
                {
                    if (item.IsChecked)
                    {
                        hasCheckedItems = true;
                        break;
                    }
                }
            }
            return hasCheckedItems;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cboTargetProjects_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            bool hasCheckedItems = false;
            if (!cboTargetProjects.HasItems)
            {
                btnSelectAllProjects.IsEnabled = false;
                ExecutionEnable(false);
                return;
            }
            else
            {
                btnSelectAllProjects.IsEnabled = true;
                foreach (CheckedListItem<ProjectType> item in cboTargetProjects.Items)
                {
                    if (item.IsChecked)
                    {
                        hasCheckedItems = true;
                        break;
                    }
                }

                ExecutionEnable(hasCheckedItems && SourceSelected());
            }
        }
                
        /// <summary>
        /// Determines if both source and target collections are selected.
        /// </summary>
        /// <returns>True if both source and target collections are selected. Otherwise false.</returns>
        private bool CollectionsSelected()
        {
            return (TargetCollection != null && SourceCollection != null);
        }
                
        /// <summary>
        /// Determines if a source project is selected. If so, the manage categories and manage WITDs buttons are enabled.
        /// </summary>
        /// <returns>True if a source project is selected. Otherwise false.</returns>
        private bool SourceProjectSelected()
        {
            //check source collection, source sourceProject
            if (CurrentSourceProject != null)
            {
                btnCategory.IsEnabled = true;
                btnManageWorkItems.IsEnabled = true;
                return true;
            }
            btnCategory.IsEnabled = false;
            btnManageWorkItems.IsEnabled = false;
            return false;
        }

        /// <summary>
        /// Checks or unchecks all target projects.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelectAllProjects_Click(object sender, RoutedEventArgs e)
        {
            bool doCheck;
            String msg;

            if (!cboTargetProjects.HasItems)
            {
                _selectedTargetsCount = 0;
                ExecutionEnable(false);
            }
            else
            {
                if ((btnSelectAllProjects.Content as string) == SelectAllProjectsCaption)
                {
                    doCheck = true;
                    msg = SelectNoProjectsCaption;
                    _selectedTargetsCount = cboTargetProjects.Items.Count;
                }
                else
                {
                    doCheck = false;
                    msg = SelectAllProjectsCaption;
                    _selectedTargetsCount = 0;
                }

                ManageCheckedProjectHandlers(false, cboTargetProjects, "chkTargetsChecked", Chk_TargetsChecked, Chk_TargetsUnchecked);
                btnSelectAllProjects.Content = msg;

                foreach (CheckedListItem<ProjectType> item in cboTargetProjects.Items)
                {
                    item.IsChecked = doCheck;
                }

                ManageCheckedProjectHandlers(true, cboTargetProjects, "chkTargetsChecked", Chk_TargetsChecked, Chk_TargetsUnchecked);
                ExecutionEnable(doCheck && SourceSelected());
            }
        }

        /// <summary>
        ///  
        /// </summary>
        /// <param name="doAdd"></param>
        /// <param name="list"></param>
        /// <param name="handlerName"></param>
        /// <param name="checkedHandler"></param>
        /// <param name="uncheckedHandler"></param>
        private void ManageCheckedProjectHandlers(bool doAdd, ListBox list, String handlerName, RoutedEventHandler checkedHandler, RoutedEventHandler uncheckedHandler)
        {

            foreach (CheckedListItem<ProjectType> item in list.Items)
            {
                ListBoxItem myListBoxItem = (ListBoxItem)(list.ItemContainerGenerator.ContainerFromItem(item));
                if (myListBoxItem != null)
                {
                    ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);

                    DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
                    CheckBox itemCheck = (CheckBox)myDataTemplate.FindName(handlerName, myContentPresenter);

                    if (doAdd)
                    {
                        itemCheck.Checked += checkedHandler;
                        itemCheck.Unchecked += uncheckedHandler;
                    }
                    else
                    {
                        itemCheck.Checked -= checkedHandler;
                        itemCheck.Unchecked -= uncheckedHandler;
                    }
                }
            }
        }

        /// <summary>
        ///  
        /// </summary>
        /// <param name="doAdd"></param>
        /// <param name="list"></param>
        /// <param name="handlerName"></param>
        /// <param name="checkedHandler"></param>
        /// <param name="uncheckedHandler"></param>
        private void ManageCheckedWITDHandlers(bool doAdd, ListBox list, String handlerName, RoutedEventHandler checkedHandler, RoutedEventHandler uncheckedHandler)
        {

            foreach (CheckedListItem<WorkItemType> item in list.Items)
            {
                ListBoxItem myListBoxItem = (ListBoxItem)(list.ItemContainerGenerator.ContainerFromItem(item));
                if (myListBoxItem != null)
                {
                    ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);

                    DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
                    CheckBox itemCheck = (CheckBox)myDataTemplate.FindName(handlerName, myContentPresenter);

                    if (doAdd)
                    {
                        itemCheck.Checked += checkedHandler;
                        itemCheck.Unchecked += uncheckedHandler;
                    }
                    else
                    {
                        itemCheck.Checked -= checkedHandler;
                        itemCheck.Unchecked -= uncheckedHandler;
                    }
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="childItem"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is childItem)
                    return (childItem)child;
                else
                {
                    childItem childOfChild = FindVisualChild<childItem>(child);
                    if (childOfChild != null)
                        return childOfChild;
                }
            }
            return null;
        }

        /// <summary>
        /// Terminates this application.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnQuit_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown(0);
            hider.Close();
            
        }

        /// <summary>
        /// Cancels an in-progress execution.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            ImportWorker.CancelAsync();

            Logger.Add("Cancelled");
            Cleanup();
        }
                
        /// <summary>
        /// Enables or disables buttons based on specified argument value. 
        /// <para></para><para></para>
        /// Prior to execution, most buttons are enabled so the user can make selections. When execution starts, controls are disabled to prevent 
        /// the UI from changing until finished.
        /// </summary>
        /// <param name="enabled">True to enable buttons. Otherwise false.</param>
        private void AllButtons(bool enabled)
        {
            if (enabled) { btnQuit.Visibility = System.Windows.Visibility.Visible; btnCancel.Visibility = System.Windows.Visibility.Hidden; }
            else { btnQuit.Visibility = System.Windows.Visibility.Hidden; btnCancel.Visibility = System.Windows.Visibility.Visible; }

            btnSourceCollection.IsEnabled = enabled;
            btnTarget.IsEnabled = enabled;
            ExecutionEnable(!enabled);
            btnSetBackupFolder.IsEnabled = enabled;
            
            if (enabled)
            {
                lblStatus.Visibility = System.Windows.Visibility.Hidden;
                ProgressImport.Visibility = System.Windows.Visibility.Hidden;
            }
            else
            {
                lblStatus.Visibility = System.Windows.Visibility.Visible;
                ProgressImport.Visibility = System.Windows.Visibility.Visible;
            }
        }

        /// <summary>
        /// Removes the XML markup to prepare the query to save.
        /// </summary>
        /// <param name="queryText">The query contents to normalize.</param>
        /// <returns>The normalized query text.</returns>
        private String NormalizeQuery(String queryText)
        {
            queryText = queryText.Substring(queryText.IndexOf("<Wiql>") + 6);
            queryText = queryText.Replace("</Wiql>", "").Replace("</WorkItemQuery>", "");
            queryText = queryText.Replace("&lt;", "<").Replace("&gt;", ">");

            return queryText;
        }

        /// <summary>
        /// Prepares a query for copy.
        /// </summary>
        /// <param name="item">The source query.</param>
        /// <param name="targetFolder">The target query folder passed back to caller.</param>
        /// <param name="xml">The actual XDocument XML of the query, passed back to caller.</param>
        /// <param name="sourceProject"></param>
        /// <returns>True if successful. Otherwise False.</returns>
        private bool CreateNewQuery(QueryDefinition item, out String targetFolder, out XDocument xml, Project sourceProject)
        {
            // get the source query
            xml = new XDocument(new XDeclaration("1.0", "utf-8", null),
                  new XElement("WorkItemQuery",
                  new XAttribute("Version", "1"),
                  new XElement("Wiql", item.QueryText)));

            targetFolder = item.Path;

            // make path anonymous, maintain case
            if (targetFolder.ToUpper().Contains(sourceProject.Name.ToUpper()))
            {
                targetFolder = targetFolder.Substring((sourceProject.Name + "/").Length);
            }

            return true;
        }

        /// <summary>
        /// Determines if at least one target sourceProject is checked.
        /// </summary>
        /// <returns>True if at least one target sourceProject is checked. Otherwise false.</returns>
        private bool TargetProjectsChecked()
        {
            //checks to see if at least one target sourceProject is checked
            foreach (CheckedListItem<ProjectType> currentProject in cboTargetProjects.Items)
            {
                if (currentProject.IsChecked)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Deletes the queries in the specified project.
        /// </summary>
        /// <param name="targetProject">The project from which to delete the queries.</param>
        /// <param name="progress">The ImportProgress object used to report progress during this action.</param>
        private void DeleteQueries(Project targetProject, ImportProgress progress)
        {
            QueryHierarchy allQueries = targetProject.QueryHierarchy;
            var rootSource = (QueryFolder)allQueries;
            ProgressManager.FormatString = QueryDeleteLogEntry;
            ProgressUpdate(progress, "Deleting queries from project " + targetProject.Name, null, null, null, null, false);
            foreach (var item in rootSource)
            {
                if (item is QueryDefinition)
                {
                    item.Delete();
                }
                else
                {
                    DeleteFolderQueries((QueryFolder)item);
                }
            }
            ProgressUpdate(progress, "Deleting queries from project " + targetProject.Name + "...success", null, null, null, null, true);
        }

        /// <summary>
        /// Deletes all the queries in the specified folder and all folders under it.
        /// </summary>
        /// <param name="folder">The folder from which to delete queries.</param>
        private void DeleteFolderQueries(QueryFolder folder)
        {
            foreach (var item in folder)
            {
                if (item is QueryDefinition)
                {
                    item.Delete();
                }
                else
                {
                    DeleteFolderQueries((QueryFolder)item);
                }
            }
        }

        /// <summary>
        /// Deletes the specified work item query from the specified query hierarchy.
        /// </summary>
        /// <param name="queryName">The name of the query to delete.</param>
        /// <param name="queries">The hierarchy that contains the query to delete.</param>
        private void DeleteQuery(string queryName, QueryHierarchy queries)
        {
            var query = queries.Find(queryName);
            if (query != null)
            {
                // delete query
                query.Delete();
            }
        }

        /// <summary>
        /// Determines the number of work item queries for th e specified project.
        /// </summary>
        /// <param name="sourceProject">The project to count queries in.</param>
        /// <returns>The number of work item queries in the specified project.</returns>
        private int QueryCount(Project sourceProject)
        {
            Folder = "Team Queries";

            // get query hierarchy - Source
            HierarchySource = sourceProject.QueryHierarchy;

            var rootSource = (QueryFolder)HierarchySource;
            var parentPathSource = string.Empty;


            // get root folder if specified
            var item = HierarchySource.Find(Folder);
            if (item == null)
            {
                //add an error and log it
                //throw new Exception(string.Format(ResourceStrings.ErrorUnknownFolder, Folder));
                ProgressUpdate(ProgressManager, "Error locating Team Queries folder for " + sourceProject.Name + ".", null, null, null,
                               new Exception("Error locating team Queries folder for " + sourceProject.Name), false, true, false);
                return 0;
            }

            if (!(item is QueryFolder))
            {
                //add an error and log it
                //throw new Exception(string.Format(ResourceStrings.ErrorNotAFolder, Folder));
                ProgressUpdate(ProgressManager, "Error locating Team Queries folder for " + sourceProject.Name + ".", null, null, null,
                               new Exception("Error locating team Queries folder for " + sourceProject.Name), false, true, false);
                return 0;
            }

            rootSource = (QueryFolder)item;
            // copy the source queries to the target
            // recursive - all queries AND folders/queries in rootSource, else just the queries in rootSource
            CountAllQueries(rootSource, parentPathSource, sourceProject);

            return _queryCount;
        }

        /// <summary>
        /// Counts all the queries in the specified project.
        /// </summary>
        /// <param name="folder">The root query folder from which to start counting queries.</param>
        /// <param name="parentPath">The parent folder of the current folder being traversed.</param>
        /// <param name="sourceProject">The project from which to count queries.</param>
        private void CountAllQueries(QueryFolder folder, string parentPath, Project sourceProject)
        {
            foreach (var item in folder)
            {
                if (item is QueryDefinition)
                {
                    _queryCount += 1;
                }
                else
                {
                    CountAllQueries((QueryFolder)item, parentPath + item.Name + "/", sourceProject);
                }
            }
        }

        /// <summary>
        /// Copies all queries from a source project to a target project.
        /// </summary>
        /// <param name="sourceProject">The project from which queries are copied.</param>
        /// <param name="targetProject">The project to which queries are copied.</param>
        /// <param name="onDup">The options that govern how to copy queries, such as when a duplicate occurs and whether to delete extraneous queries.</param>
        /// <param name="progress">The ImportProgress object to report progress during this action.</param>
        private void CopyQueries(Project sourceProject, Project targetProject, QueryCopyOptions onDup, ImportProgress progress)
        {
            Folder = "Team Queries";

            // get query hierarchy - Source
            HierarchySource = sourceProject.QueryHierarchy;

            // get query hierarchy - Target
            QueryImporter = targetProject.QueryHierarchy;

            var rootSource = (QueryFolder)HierarchySource;
            var parentPathSource = string.Empty;


            // get root folder if specified
            var item = HierarchySource.Find(Folder);
            if (item == null)
            {
                //add an error and log it
                //throw new Exception(string.Format(ResourceStrings.ErrorUnknownFolder, Folder));
            }

            if (!(item is QueryFolder))
            {
                //add an error and log it
                //throw new Exception(string.Format(ResourceStrings.ErrorNotAFolder, Folder));
            }

            rootSource = (QueryFolder)item;
            // copy the source queries to the target
            // recursive - all queries AND folders/queries in rootSource, else just the queries in rootSource
            CopyAllQueries(rootSource, parentPathSource, sourceProject, onDup, progress);
        }

        /// <summary>
        /// Copies one or more work item queries from one collection and sourceProject to another 
        /// collection and sourceProject. if recursion is specified, all queries under the specified folder 
        /// are imported.
        /// </summary>
        /// <param name="folder">The source folder to copy queries from.</param>
        /// <param name="parentPath">The source folder's parent path.</param>
        /// <param name="sourceProject">The project containing the work item queries to copy..</param>
        /// <param name="onDupAction">Specifies the action to take when a query with a duplicate name is encountered.</param>
        /// <param name="progress"></param>
        private void CopyAllQueries(QueryFolder folder, string parentPath, Project sourceProject, QueryCopyOptions onDupAction, ImportProgress progress)
        {
            foreach (var item in folder)
            {
                if (item is QueryDefinition)
                {
                    String targetFolder;
                    XDocument newQuery;
                    ProgressManager.FormatString = QueryImportLogEntry;
                    ProgressUpdate(progress, "Importing query " + item.Name, null, null, null, null, false);
                    if (CreateNewQuery((QueryDefinition)item, out targetFolder, out newQuery, sourceProject))
                    {
                        //copy current query to the correct folder
                        switch (CopyQuery(newQuery, targetFolder, parentPath, onDupAction))
                        {
                            case ImportResult.Success:
                                {
                                    ProgressUpdate(progress, "Importing query " + item.Name + "...success", null, null, null, null, true);
                                    break;
                                }
                            case ImportResult.Exception:
                                {
                                    ProgressUpdate(progress, "Error importing query " + item.Name, null, null, null, null, false, true);
                                    break;
                                }
                            case ImportResult.InvalidOnDup:
                                {
                                    ProgressUpdate(progress, "Duplicate query error on query " + item.Name, null, null, null, null, false, true);
                                    break;
                                }
                            case ImportResult.MissingAreasOrIterations:
                                {
                                    ProgressUpdate(progress, "Error importing query from missing area or iteration node in query " + item.Name, null, null, null, null, false, true);
                                    break;
                                }
                            case ImportResult.Skipped:
                                {
                                    ProgressUpdate(progress, "Skipped importing query " + item.Name, null, null, null, null, false, true);
                                    break;
                                }
                        }
                    }
                }
                else
                {
                    //check in target sourceProject QueryHiearchy for this folder and create it if not found
                    //this is necessary because multiple folders in the path to the current query may not exist in the target sourceProject
                    //so we have to create them as we recurse over them until we get to the queries to copy.
                    //If we don't and we try to create the last folder and it's parent does not exist, it will fail.

                    // make path anonymous by removing source sourceProject name, maintain case

                    String targetFolder = item.Path;
                    if (targetFolder.ToUpper().Contains(sourceProject.Name.ToUpper()))
                    {
                        targetFolder = targetFolder.Substring((sourceProject.Name.ToUpper() + "/").Length);
                    }
                    var folderToCreate = QueryImporter.Find(targetFolder);
                    //creates the folder only if not found, otherwise returns a reference to existing folder
                    if (folderToCreate == null) { folderToCreate = QueryImporter.CreateFolder(targetFolder); QueryImporter.Save(); }
                    CopyAllQueries((QueryFolder)item, parentPath + item.Name + "/", sourceProject, onDupAction, progress);
                }
            }
        }

        /// <summary>
        /// Imports the specified Work Item Query (WIQ) into the specified target folder, sourceProject, and collection. If the query 
        /// already exists, it takes the specified action (overwrite, rename, skip).
        /// </summary>
        /// <param name="newQuery">The work item query to import.</param>
        /// <param name="targetFolder">The folder to place the imported query.</param>
        /// <param name="parentPath">The folder's parent path.</param>
        /// <param name="onDup">The <see cref="QueryCopyOptions"/> to take if the named query already exists.</param>
        public ImportResult CopyQuery(XDocument newQuery, String targetFolder, String parentPath, QueryCopyOptions onDup)
        {
            //defined here so we can reference it in the exception handler section
            QueryItem query = null;
            ImportResult result = ImportResult.Success;

            try
            {
                var doc = new XDocument(newQuery);
                var queryText = doc.ToString();

                // get or create query folder
                var index = targetFolder.LastIndexOf('/');
                var folderPath = (index == -1 ? string.Empty : targetFolder.Substring(0, index));
                var queryName = targetFolder.Substring(index + 1);
                var folder = QueryImporter.CreateFolder(folderPath);

                // create or update query
                query = folder.Find(queryName);

                if (query == null)
                {
                    // new query in the target sourceProject, so no OnDup action here
                    // normalize the query text to pass to constructor
                    queryText = NormalizeQuery(queryText);
                    queryText = queryText.Replace(HierarchySource.Project.Name, QueryImporter.Project.Name);
                    query = new QueryDefinition(queryName, queryText, folder);
                    QueryImporter.Save();
                }
                else
                {
                    if (!(query is QueryDefinition))
                    {
                        // item is not a query
                        //throw new Exception(string.Format(ResourceStrings.ErrorFolderAlreadyExists, folder));
                    }
                    else
                    {
                        if (onDup.Options != QueryOptions.Skip)
                        {
                            // update query, but only if we'll be using it
                            // normalize the query text to save
                            queryText = NormalizeQuery(queryText);
                        }

                        switch (onDup.Options)
                        {
                            case QueryOptions.Overwrite:
                                ((QueryDefinition)query).QueryText = queryText;
                                QueryImporter.Save();

                                break;

                            case QueryOptions.RenameNew:
                                //create a sufficiently unique name
                                var renamedQueryNew = new QueryDefinition(GetNewName(queryName, "renameNew"), queryText, folder);
                                QueryImporter.Save();

                                break;

                            case QueryOptions.RenameOld:
                                //create a sufficiently unique name
                                String renamedQueryOld = GetNewName(((QueryDefinition)query).Name, "renameOld");
                                ((QueryDefinition)query).Name = renamedQueryOld;

                                var newQueryDef = new QueryDefinition(queryName, queryText, folder);
                                QueryImporter.Save();

                                break;

                            case QueryOptions.Skip:
                                result = ImportResult.Skipped;

                                break;

                            default: // should never get here, but good practice to have default
                                result = ImportResult.InvalidOnDup;

                                break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg;

                //check for the most common error
                if (ex.Message.Contains(MissingClassificationNodeErrorCode))
                {
                    result = ImportResult.MissingAreasOrIterations;
                    msg = "Iteration or Area node is missing from target project. Ensure all Area and Iteration nodes from source project that are referenced from queries" +
                          " being copied are present in the target project." + Environment.NewLine + ex.Message;
                }
                else
                {
                    result = ImportResult.Exception;
                    msg = ex.Message;
                }

            }

            return result;
        }
        
        /// <summary>
        /// Opens a select folder dialog to set the backup folder.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetBackupFolder_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.FolderBrowserDialog dialog = new System.Windows.Forms.FolderBrowserDialog();
            dialog.Description = "Select folder for backup of artifacts";
            dialog.ShowNewFolderButton = true;
            
            if (!String.IsNullOrEmpty(BackupFolder))
            {
                dialog.SelectedPath = BackupFolder;
            }
            else
            {
                dialog.RootFolder = Environment.SpecialFolder.MyDocuments;
            }

            ObscureMainWindow(null);

            try
            {
                System.Windows.Forms.DialogResult result = dialog.ShowDialog();

                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    BackupFolder = dialog.SelectedPath;
                    txtBackupFolder.Text = BackupFolder;
                    WITD.Configuration.SaveToConfig(KeyAppConfigbackupFolder, BackupFolder);
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                UnobscureMainWindow();
            }
        }

       
        /// <summary>
        /// Enables or disables options depending on state of the "Include global list" check box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void global_Click(object sender, RoutedEventArgs e)
        {
            gbGlobalList.IsEnabled = global.IsChecked.Value;
            rbAddMissingGlobalLists.IsEnabled = global.IsChecked.Value;
            rbOverwriteAllGlobalLists.IsEnabled = global.IsChecked.Value;
            rbAddToWITDOnly.IsEnabled = global.IsChecked.Value;
            
            if (global.IsChecked.Value) { rbAddMissingGlobalLists.IsChecked = true; } //default = add missing global lists
        }

        /// <summary>
        /// Enables or disables options depending on state of the "Include queries" check box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkIncludeQueries_Click(object sender, RoutedEventArgs e)
        {
            gbIncludeQueries.IsEnabled = chkIncludeQueries.IsChecked.Value;
            rbOverwriteQuery.IsEnabled = chkIncludeQueries.IsChecked.Value;
            rbRenameNewQuery.IsEnabled = chkIncludeQueries.IsChecked.Value;
            rbRenameOldQuery.IsEnabled = chkIncludeQueries.IsChecked.Value;
            rbSkipQuery.IsEnabled = chkIncludeQueries.IsChecked.Value;
            chkRemoveQueries.IsEnabled = chkIncludeQueries.IsChecked.Value;
            if (chkIncludeQueries.IsChecked.Value) { rbRenameOldQuery.IsChecked = true; } //default = rename old query
        }

        /// <summary>
        /// Enables or disables options depending on state of the "Include iterations" check box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkIncludeIterations_Click(object sender, RoutedEventArgs e)
        {
            gbIncludeIterations.IsEnabled = chkIncludeIterations.IsChecked.Value;
            rbOverwriteIterations.IsEnabled = chkIncludeIterations.IsChecked.Value;
            rbAddMissingIterations.IsEnabled = chkIncludeIterations.IsChecked.Value;
            if (chkIncludeIterations.IsChecked.Value) { rbAddMissingIterations.IsChecked = true; } //default = add missing iterations
        }

        /// <summary>
        /// Enables or disables options depending on state of the "Work Item type options" check box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkWITOptions_Click(object sender, RoutedEventArgs e)
        {
            gbWITs.IsEnabled = chkIncludeWITDs.IsChecked.Value;
            rbOverwriteWIT.IsEnabled = chkIncludeWITDs.IsChecked.Value;
            rbRenameNewWIT.IsEnabled = chkIncludeWITDs.IsChecked.Value;
            rbRenameOldWIT.IsEnabled = chkIncludeWITDs.IsChecked.Value;
            rbSkipWIT.IsEnabled = chkIncludeWITDs.IsChecked.Value;
            if (chkIncludeWITDs.IsChecked.Value) { rbOverwriteWIT.IsChecked = true; } //default = overwrite
        }

        /// <summary>
        /// Updates the status of operations by moving the progress bar and updating the textboxes with the correct names of projects and work item types.
        /// </summary>
        /// <param name="progress">The Object that contains the properties and methods for communicating status.</param>
        /// <param name="status">The text of the current operation</param>
        /// <param name="sourceProject">The source project</param>
        /// <param name="targetProject">The current target project</param>
        /// <param name="wit">The current work item type</param>
        /// <param name="currentError">The current Exception, if one exists. Otherwise null.</param>
        /// <param name="doInc">True to increment the progress bar.</param>
        /// <param name="isError">True if the current invocation is for an exception. Default = null.</param>
        /// <param name="isComplete">True if the all scheduled operations have completed and the progress bar should move to 100%. Default = false.</param>
        /// <param name="waitTimeMS">The delay in milliseconds. Default = 50 ms.</param>
        public void ProgressUpdate(ImportProgress progress,
                                    String status,
                                    String sourceProject,
                                    String targetProject,
                                    String wit,
                                    Exception currentError,
                                    bool doInc,
                                    bool isError = false,
                                    bool isComplete = false,
                                    int waitTimeMS = 50)
        {
            progress.StatusText = status;
            if (sourceProject != null) { progress.SourceProject = sourceProject; }
            if (targetProject != null) { progress.CurrentProject = targetProject; }
            if (currentError != null) { progress.CurrentError = currentError; }
            if (wit != null) { progress.CurrentWIT = wit; }

            if (doInc) 
            {   
                progress.Inc();
            }

            ImportWorker.ReportProgress(progress.PercentComplete, progress);
            progress.HasErrors = isError;
            System.Threading.Thread.Sleep(waitTimeMS);
        }
        
        /// <summary>
        /// Opens a new form to manage link types.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLinks_Click(object sender, RoutedEventArgs e)
        {
            AddNewLinkTypeForm addLinkForm = new AddNewLinkTypeForm(SourceStore, hider);
            ObscureMainWindow(addLinkForm);
            bool? result = addLinkForm.ShowDialog();
            UnobscureMainWindow();
        }

        /// <summary>
        /// Places an opaque window over the current window to focus the user's attention on the new form about to open and claim focus.
        /// </summary>
        private void ObscureMainWindow(Window newWindow)
        {
            Topmost = false;
            toolWindow = newWindow;
            if (newWindow != null) newWindow.Topmost = true;
            hider.Height = this.Height;
            hider.Width = this.Width;
            hider.Top = this.Top;
            hider.Left = this.Left;
            hider.Cursor = Cursors.Wait;
            hider.Show();
        }

        /// <summary>
        /// Hides the opaque window without destroying it, making the current form the focus once again.
        /// </summary>
        private void UnobscureMainWindow()
        {
            hider.Hide();
            toolWindow = this;
            this.Topmost = true;
            this.Focus();
        }

        /// <summary>
        /// Opens a new form to manage categories.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCategory_Click(object sender, RoutedEventArgs e)
        {

            
            int index = cob_projects.SelectedIndex;
            Categories categoryForm = new Categories( SourceStore, SourceProjectType.Name, hider);

            ObscureMainWindow(categoryForm);
            categoryForm.ShowDialog();

            if (categoryForm.DidDestroy || categoryForm.DidImport || categoryForm.DidRename)
            {
                Reconnect(CurrentSourceProject, index);
            }

            UnobscureMainWindow();
        }
        
        /// <summary>
        /// Performs a cache refresh and synchronization to a specified work item store.
        /// </summary>
        /// <param name="store">The WorkItemStore from which to cache.</param>
        private void Sync(WorkItemStore store)
        {
            store.RefreshCache();
            store.SyncToCache();
        }

        /// <summary>
        /// Reconnects to the source project collection in order to refresh the display with updated information.
        /// </summary>
        /// <param name="project">The project from which to extract the project collection in order to reconnect.</param>
        /// <param name="indexOfPreviouslySelectedProject">The index of the source project to reconnect to.</param>
        private void Reconnect(Project project, int indexOfPreviouslySelectedProject)
        {
            SourceCollection = new TfsTeamProjectCollection(project.Store.TeamProjectCollection.Uri);
            SourceCollection.EnsureAuthenticated();

            SourceStore = (WorkItemStore)SourceCollection.GetService(typeof(WorkItemStore));
            
            lblSourceCollection.Text = SourceCollection.Uri.ToString();
            GetProject();
            if (indexOfPreviouslySelectedProject >= 0)
                cob_projects.SelectedIndex = indexOfPreviouslySelectedProject;
        }

        /// <summary>
        /// Opens a file open dialog to select a log file for viewing.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnViewLogs_Click(object sender, RoutedEventArgs e)
        {
            ObscureMainWindow(null);
            Logger.SelectLog();
            UnobscureMainWindow();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Activated(object sender, EventArgs e)
        {
            if (hider.Visibility == System.Windows.Visibility.Visible)
            {
                hider.Topmost = true;
                if (toolWindow != null)
                {
                    toolWindow.Topmost = true;
                    toolWindow.Focus();
                   
                }
                    
            }

           
                
        }

        /// <summary>
        /// Opens a new form to manage WorkItemTypes and work items.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnManageWorkItems_Click(object sender, RoutedEventArgs e)
        {
                WorkItemsList wiList = new WorkItemsList(SourceStore.Projects[SourceProjectType.Name], hider);
                ObscureMainWindow(wiList);
                this.IsEnabled = false;
                wiList.ShowDialog();
                this.IsEnabled = true;
                UnobscureMainWindow();
        }
    } 
}


    



    
    


    